sak

overview

data_sak_raw |> 
  ggplot(aes(Layers)) +
  geom_histogram(binwidth = 1)

quantile(data_sak_raw$Layers, na.rm = TRUE) # 1,2,3,4,15
  0%  25%  50%  75% 100% 
   1    2    3    4   15 
quantile(data_sak_raw$class, na.rm = TRUE) # 1,2,3,4,15
  0%  25%  50%  75% 100% 
  58   84   91  111  124 
data_sak_raw |> 
  ggplot(aes(Layers, Functions)) +
  geom_count(alpha = 0.3) 

  # select(which(!colSums(data_sak_raw, na.rm=TRUE) %in% 0) | is.character()) # 合計が0, 1 もしくはnrow(), nrow()-1のものはuninformativeなので削除してよいはず
df_sak |> 
  filter(Price <30000, class < 112) |> 
  ggplot(aes(x = Price, y = Functions, colour = class, group = class)) +
  geom_point() +
  geom_smooth(method = "lm") 
`geom_smooth()` using formula 'y ~ x'
Warning in qt((1 - level)/2, df) : NaNs produced
Warning in max(ids, na.rm = TRUE) :
  no non-missing arguments to max; returning -Inf

corresp

data https://www1.doshisha.ac.jp/~mjin/R/Chap_26/26.html use FactoMineR instead of MASS to get eigen values


df_sak_factor <- df_sak |> 
  # filter(Price ) |> # remove swissChampXAVT
  select(Layer1:`MAT_Scale:BrassWood`) |> 
  mutate(across(everything(), as.factor))
sak_mca <- MCA(df_sak_factor, ncp = 2, graph = TRUE)


df_sak_mca_coord <- sak_mca$ind$coord |> 
  as_tibble() |> 
  transmute(mca_x = `Dim 1`, mca_y = `Dim 2`)


# http://www.sthda.com/english/articles/31-principal-component-methods-in-r-practical-guide/114-mca-multiple-correspondence-analysis-in-r-essentials/
# extract eigen value
sak_corresp_eigen_values <- get_eigenvalue(sak_mca)
# check eigen v
fviz_screeplot(sak_mca, addlabels = TRUE, ylim = c(0, 20))
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     

# variance explained by dim1 and dim2 are:
# they are later used in the ggplot
sak_corresp_dim1_ev <- sak_corresp_eigen_values[1, 2]
sak_corresp_dim2_ev <- sak_corresp_eigen_values[2, 2]

# coordinates
df_sak_factor2 <- bind_cols(df_sak, df_sak_mca_coord)

all corresp

facet grid

cut the trees

https://uc-r.github.io/hc_clustering

df_fviz_wss <- df_sak_corresp |> 
  fviz_nbclust(FUN = hcut, method = "wss", k.max = 30)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
df_fviz_wss <- df_sak_corresp |> 
  fviz_nbclust(FUN = hcut, method = "wss", k.max = 30)
df_fviz_wss2 <- df_fviz_wss$data |> 
  mutate(clusters = as.numeric(clusters))
df_fviz_wss2 |>   
  ggplot(aes(clusters, y)) +
  geom_line() +
  # geom_point() +
  ylab("Total WSS") +
  xlab("No of clusters k") +
  theme_classic(base_family = "Fira Code")

UPGMA

jaccard (dont use)

hamming

SplitsTree4のUncorrectedPがHamming距離らしいので、{cultevo}を使ってHamming距離を算出する。しかし全然違う結果になる・・・なぜ?

sak_dist <- hammingdists(df_sak_factor) # 5sec. sometimes it's finished very fast but it means it's not running...? in that case, restart R

sak_upgma <- sak_dist |>
  hclust(method = "average") |> # average = UPGMA
   as.phylo()
sak_upgma$tip.label <- df_sak$short_name
sak_upgma |> 
  plot.phylo(type = "u", use.edge.length = TRUE, lab4ut = "axial")

NJ

sak_nj <- sak_dist |> nj()
sak_nj$tip.label <- df_sak$short_name
sak_nj |> 
  plot.phylo(type = "u", use.edge.length = TRUE, lab4ut = "axial")

join traits

df_sak_rowid <- df_sak |>
  rowid_to_column(var = "node") |> 
  mutate(node = node |> as.integer())

UPGMA

df_sak_upgma <- sak_upgma |>
  as_tibble() |> 
  left_join(df_sak_rowid, by = "node") |> 
  select(parent:class, short_name, Functions, width, weight, id) |> 
    mutate(short_name = if_else(is.na(short_name), node |> as.character(), short_name)) |> 
 column_to_rownames(var = "short_name") |> 
  mutate(node = node |> as.numeric()) |> 
  select(node, class, id, Functions, weight, width) # temporarily limite
sak_upgma$tip.label <- df_sak$short_name

NJ

df_sak_nj <- sak_nj |> 
  as_tibble() |> 
  left_join(df_sak_rowid, by = "node") |> 
  select(parent:class, short_name, Functions, width, weight, id) |> 
  mutate(short_name = if_else(is.na(short_name), node |> as.character(), short_name)) |> 
  column_to_rownames(var = "short_name") |> 
  mutate(node = node |> as.numeric()) |> 
  select(node, class, id, Functions, weight, width)

ancestral phenotype reconstruction using fastAnc

phytools fastAnc

kyoutsuu

# create a named number vector of weights (in gram) of SAKs
sak_trait_weight <- df_sak_upgma |> 
  select(weight) |> 
  as.matrix()
# change the matrix into a vector, then cull unwanted nodes after 57
sak_trait_weight_named_num <- sak_trait_weight[, 1][1:nrow(df_sak)]

UPGMA

# now fit the data to nodes using UPGMA tree
sak_fit_upgma_weight <- fastAnc(
  sak_upgma, 
  sak_trait_weight_named_num,
  vars = TRUE, 
  CI = TRUE)
df_sak_upgma$weight[(nrow(df_sak) + 1): nrow(df_sak_upgma)] <- sak_fit_upgma_weight$ace

nj

sak_fit_nj_weight <- fastAnc(
  sak_nj, 
  sak_trait_weight_named_num,
  vars = TRUE,
  CI = TRUE)
df_sak_nj$weight[(nrow(df_sak) + 1): nrow(df_sak_nj)] <- sak_fit_nj_weight$ace

join tree with df

# join with tree. seems impossible but possible :D
sak_upgma2 <- full_join(sak_upgma, df_sak_upgma, by = "node")
sak_nj2 <- full_join(sak_nj, df_sak_nj, by = "node")

Plot using ggtree

UPGMA hamming

sak_upgma2 |> 
  ggtree(layout = "ape", aes(colour = weight)) +
  geom_tiplab(aes(label = paste0(class, label),), family = "Fira Code", size = 2, hjust = -.15) + # model name (e.g., Explorer)
  geom_tiplab(aes(label = weight |> round(0)), family = "Fira Code", size = 1, hjust = 1) + # weight of models
  geom_nodelab(aes(label = weight |> round(0)), family = "Fira Code", size = 1, colour = "black") + # estimated weights at nodes
  geom_treescale() + 
  scale_size_area(max_size = 5) +
  scale_colour_viridis_c(limits = c(15, 206)) +
  coord_cartesian(clip = 'off') +
  theme_tree(
    plot.margin=margin(40, 80, 40, 60), 
  ) +
  theme(
    legend.position = c(0.05, .8),
    legend.key.size = unit(2, 'mm'),
    ) 
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

NJ

sak_nj2 |> 
  ggtree(layout = "ape", aes(colour = weight)) +
  geom_tiplab(aes(label = paste0(class, label),), family = "Fira Code", size = 3, hjust = -.3) + # model name (e.g., Explorer)
  geom_tiplab(aes(label = weight |> round(0)), family = "Fira Code", size = 2, hjust = 1) + # weight of models
  geom_nodelab(aes(label = weight |> round(0)), family = "Fira Code", size = 2, vjust = 1) + # estimated weights at nodes
  geom_treescale() + 
  scale_size_area(max_size = 5) +
  scale_colour_viridis_c(limits = c(15, 206)) +
  coord_cartesian(clip = 'off') +
  theme_tree(
    plot.margin=margin(120, 40, 40, 40), 
  ) +
  theme(
    legend.position = c(0.1, .9),
    legend.key.size = unit(2, 'mm'),
    ) 
Coordinate system already present. Adding new coordinate system, which will replace the existing one.

sak_nj2 |> 
  root(outgroup = 45, edgelabel = TRUE) |> 
  ggtree(aes(colour = weight)) +
  geom_tiplab(aes(label = paste0(class, label),), family = "Fira Code", size = 3, hjust = -.3) + # model name (e.g., Explorer)
  geom_tiplab(aes(label = weight |> round(0)), family = "Fira Code", size = 2, hjust = 1) + # weight of models
  geom_nodelab(aes(label = weight |> round(0)), fill = "white",  family = "Fira Code", size = 2) + # estimated weights at nodes
  geom_treescale() + 
  scale_size_area(max_size = 5) +
  scale_colour_viridis_c(limits = c(15, 206)) +
  # coord_cartesian(clip = 'off') +
  theme_tree(
    plot.margin=margin(40, 40, 40, 40), 
  ) +
  theme(
    legend.position = c(0.15, .5),
    legend.key.size = unit(2, 'mm'),
    ) 

create phyDat data for categorical phenotype reconstruction

df_sak_factor_with_name <- df_sak_factor |> 
  mutate(name = df_sak$short_name) |> 
  relocate(name)

phyDat_sak <-
  df_sak_factor_with_name |> 
  rownames_to_column() |> 
  pivot_longer(-name, 'variable', 'value') |> 
  pivot_wider(variable, name) |> 
  filter(variable != "rowname") |> 
  mutate(across(everything(), as.character)) |> 
  column_to_rownames(var = "variable") |>
  phyDat(type = "USER", levels = c("0", "1")) 
phyDat_sak |> View()

neighbornet

plot isn’t really good. use SplitsTree4 using write.nexus()?

sak_dist2 <- phyDat_sak |> dist.hamming() # must be phyDat
Error in dist.hamming(phyDat_sak) : object 'phyDat_sak' not found

plot NN

sak_nn |> ggsplitnet(aes(x, y))+
  geom_splitnet(size = .1) +
  geom_tiplab2(data =  df_sak_nn, aes(colour = class), size = 2, family = "Fira Code") +
  theme_tree() +
  xlim(-.25, .35) +
  ylim(-.25,.25)
Error in FUN(X[[i]], ...) : object 'angle' not found

getwd()
df_sak_factor_with_name |> 
  write_tsv("./data/sak_factor_for_mesquite_nex.tsv")
sak_nj |> write.nexus("data/sak_factor.nex")

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKCiMgc2FrCgojIyBvdmVydmlldwoKYGBge3J9CmRhdGFfc2FrX3JhdyB8PiAKICBnZ3Bsb3QoYWVzKExheWVycykpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDEpCnF1YW50aWxlKGRhdGFfc2FrX3JhdyRMYXllcnMsIG5hLnJtID0gVFJVRSkgIyAxLDIsMyw0LDE1CnF1YW50aWxlKGRhdGFfc2FrX3JhdyRjbGFzcywgbmEucm0gPSBUUlVFKSAjIDEsMiwzLDQsMTUKZGF0YV9zYWtfcmF3IHw+IAogIGdncGxvdChhZXMoTGF5ZXJzLCBGdW5jdGlvbnMpKSArCiAgZ2VvbV9jb3VudChhbHBoYSA9IDAuMykgCgpgYGAKCgoKYGBge3J9CmRmX3NhayA8LSBkYXRhX3Nha19yYXcgfD4gCiAgbXV0YXRlKGlkID0gcGFzdGUwKGNsYXNzLCBzaG9ydF9uYW1lKSkgfD4gCiAgIyBmaWx0ZXIoc2hvcnRfbmFtZSAhPSAiT1VULkJ1Y2siKSB8PiAKICBtdXRhdGUobGF5ZXJfY2F0ZWdvcnkgPSBjYXNlX3doZW4oCiAgICBMYXllcnMgPT0gMSB+ICJMYXllcjEiLAogICAgTGF5ZXJzID09IDIgfiAiTGF5ZXJzMiIsCiAgICBMYXllcnMgPT0gMyB+ICJMYXllcnMzIiwKICAgIExheWVycyA9PSA0IH4gIkxheWVyczQiLAogICAgTGF5ZXJzID4gNCB+ICJMYXllcnNNb3JlVGhhbjQiCiAgICApLCAjIGxheWVy44Gu5pWw44Gn77yU44Gk44G744Gp77yf44Gr5aC05ZCI5YiG44GR44GX44Gm5paw44GX44GE5YiX44KSMC8x44Gn44Gk44GR44Gm44GE44GN44Gf44GELiB1c2UgcXVhbnRpbGUoKQogICAgdmFsdWUgPSAxTAogICkgfD4gICAjIFRPRE86IAogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBsYXllcl9jYXRlZ29yeSwgdmFsdWVzX2Zyb20gPXZhbHVlLCB2YWx1ZXNfZmlsbCA9IDBMKSB8PiAKICByZWxvY2F0ZShzdGFydHNfd2l0aCgiTGF5ZXIiKSwgLmJlZm9yZSA9IExvY2spIHw+IAogIHJlbG9jYXRlKCJMYXllcjEiLCAuYWZ0ZXIgPSAiTGF5ZXJzIikgfD4gCiAgc2VsZWN0KC1zYWspCiAgCiAgCiAgIyBzZWxlY3Qod2hpY2goIWNvbFN1bXMoZGF0YV9zYWtfcmF3LCBuYS5ybT1UUlVFKSAlaW4lIDApIHwgaXMuY2hhcmFjdGVyKCkpICMg5ZCI6KiI44GMMCwgMSDjgoLjgZfjgY/jga9ucm93KCksIG5yb3coKS0x44Gu44KC44Gu44GvdW5pbmZvcm1hdGl2ZeOBquOBruOBp+WJiumZpOOBl+OBpuOCiOOBhOOBr+OBmgpkZl9zYWsgfD4gCiAgZmlsdGVyKFByaWNlIDwzMDAwMCwgY2xhc3MgPCAxMTIpIHw+IAogIGdncGxvdChhZXMoeCA9IFByaWNlLCB5ID0gRnVuY3Rpb25zLCBjb2xvdXIgPSBjbGFzcywgZ3JvdXAgPSBjbGFzcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpIAogICMgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhjbGFzcykpCmBgYAoKYGBge3J9CmRmX3NhayB8PiAKICBmaWx0ZXIoUHJpY2UgPDMwMDAwLCBjbGFzcyA8IDExMikgfD4gCiAgZ2dwbG90KGFlcyh4ID0gd2lkdGgsIHkgPSBQcmljZSwgY29sb3VyID0gd2VpZ2h0KSkgKwogIGdlb21fcG9pbnQoKSArCiAgZmFjZXRfZ3JpZChjb2xzID0gdmFycyhjbGFzcykpCmBgYAoKIyMgY29ycmVzcAoKCmRhdGEKaHR0cHM6Ly93d3cxLmRvc2hpc2hhLmFjLmpwL35tamluL1IvQ2hhcF8yNi8yNi5odG1sCnVzZSBGYWN0b01pbmVSIGluc3RlYWQgb2YgTUFTUyB0byBnZXQgZWlnZW4gdmFsdWVzCgpgYGB7ciBmaWcud2lkdGg9NSwgZmlnaC5oZWlnaHQgPSAzfQoKZGZfc2FrX2ZhY3RvciA8LSBkZl9zYWsgfD4gCiAgIyBmaWx0ZXIoUHJpY2UgKSB8PiAjIHJlbW92ZSBzd2lzc0NoYW1wWEFWVAogIHNlbGVjdChMYXllcjE6YE1BVF9TY2FsZTpCcmFzc1dvb2RgKSB8PiAKICBtdXRhdGUoYWNyb3NzKGV2ZXJ5dGhpbmcoKSwgYXMuZmFjdG9yKSkKc2FrX21jYSA8LSBNQ0EoZGZfc2FrX2ZhY3RvciwgbmNwID0gMiwgZ3JhcGggPSBUUlVFKQoKZGZfc2FrX21jYV9jb29yZCA8LSBzYWtfbWNhJGluZCRjb29yZCB8PiAKICBhc190aWJibGUoKSB8PiAKICB0cmFuc211dGUobWNhX3ggPSBgRGltIDFgLCBtY2FfeSA9IGBEaW0gMmApCgoKIyBodHRwOi8vd3d3LnN0aGRhLmNvbS9lbmdsaXNoL2FydGljbGVzLzMxLXByaW5jaXBhbC1jb21wb25lbnQtbWV0aG9kcy1pbi1yLXByYWN0aWNhbC1ndWlkZS8xMTQtbWNhLW11bHRpcGxlLWNvcnJlc3BvbmRlbmNlLWFuYWx5c2lzLWluLXItZXNzZW50aWFscy8KIyBleHRyYWN0IGVpZ2VuIHZhbHVlCnNha19jb3JyZXNwX2VpZ2VuX3ZhbHVlcyA8LSBnZXRfZWlnZW52YWx1ZShzYWtfbWNhKQojIGNoZWNrIGVpZ2VuIHYKZnZpel9zY3JlZXBsb3Qoc2FrX21jYSwgYWRkbGFiZWxzID0gVFJVRSwgeWxpbSA9IGMoMCwgMjApKQoKIyB2YXJpYW5jZSBleHBsYWluZWQgYnkgZGltMSBhbmQgZGltMiBhcmU6CiMgdGhleSBhcmUgbGF0ZXIgdXNlZCBpbiB0aGUgZ2dwbG90CnNha19jb3JyZXNwX2RpbTFfZXYgPC0gc2FrX2NvcnJlc3BfZWlnZW5fdmFsdWVzWzEsIDJdCnNha19jb3JyZXNwX2RpbTJfZXYgPC0gc2FrX2NvcnJlc3BfZWlnZW5fdmFsdWVzWzIsIDJdCgojIGNvb3JkaW5hdGVzCmRmX3Nha19mYWN0b3IyIDwtIGJpbmRfY29scyhkZl9zYWssIGRmX3Nha19tY2FfY29vcmQpCgpgYGAKCgojIyMjIGFsbCBjb3JyZXNwCgpgYGB7cn0KZGZfc2FrX2ZhY3RvcjIgfD4KICBnZ3Bsb3QoYWVzKG1jYV94LCBtY2FfeSwgbGFiZWwgPSBzaG9ydF9uYW1lLCBjb2xvdXIgPSB3ZWlnaHQpKSArCiAgIyBnZW9tX3BvaW50KGRhdGEgPSBkZl9zYWtfZmFjdG9yMiB8PiBzZWxlY3QoLWNsYXNzKSwgY29sb3VyID0gImdyZXk3MCIpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gY2xhc3MpLAogICAgICAgICAgICBmYW1pbHkgPSAiQWx0ZSBESU4gMTQ1MSBNaXR0ZWxzY2hyaWZ0IiwKICAgICAgICAgICAgZm9udGZhY2UgPSAiYm9sZCIKICAgICAgICAgICAgKSArCiAgZ2VvbV90ZXh0X3JlcGVsKAogICAgIyBmb3JjZSA9IC4zLAogICAgZm9yY2VfcHVsbCA9IDIsCiAgICBtYXgub3ZlcmxhcHMgPSAzMCwgCiAgICBtYXgudGltZSA9IDMsIAogICAgbWF4Lml0ZXIgPSAzZTUsIAogICAgc2l6ZSA9IDEuNSwKICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCAKICAgIGZhbWlseSA9ICJGaXJhIENvZGUiLAogICAgc2VnbWVudC5jb2xvdXIgPSBOQQogICAgCiAgICApICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKHRyYW5zID0gImxvZzEwIikgKwogIHNjYWxlX3NpemVfYXJlYSgpICsKICAjIGxhYnMoY29sb3VyID0gIlByaWNlICgxayB5ZW4pIiApICsKICB4bGFiKHBhc3RlMCgiRGltZW5zaW9uIDEgKCIsIHNha19jb3JyZXNwX2RpbTFfZXYgfD4gcm91bmQoZGlnaXRzID0gMSksICIlKSIpKSArCiAgeWxhYihwYXN0ZTAoIkRpbWVuc2lvbiAyICgiLCBzYWtfY29ycmVzcF9kaW0yX2V2IHw+IHJvdW5kKGRpZ2l0cyA9IDEpLCAiJSkiKSkgKwogIHRoZW1lX2J3KGJhc2VfZmFtaWx5ID0gIkZpcmEgQ29kZSIpICsKICB0aGVtZShwYW5lbC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAid2hpdGUiLCBjb2xvdXIgPSAiYmxhY2siKSkgKwogIGd1aWRlcyhjb2xvdXIgPSBndWlkZV9jb2xvdXJiYXIoYmFyd2lkdGggPSA1LCBiYXJoZWlnaHQgPSAuNSksIGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikgKwogIHRoZW1lKGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIsIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC44LCAwLjkpLAogICAgICAgIHBhbmVsLmdyaWQgPSBlbGVtZW50X2JsYW5rKCkpCgoKZ2dzYXZlKGZpbGVuYW1lID0gIm91dHB1dC9zYWtfY29ycmVzcFhZX2FsbC5wZGYiLCAKICAgICAgICAgZGV2aWNlID0gY2Fpcm9fcGRmLAogICAgICAgICBkcGkgPSAzMDAsCiAgICAgICAgIHVuaXQgPSAibW0iLAogICAgICAgaGVpZ2h0ID0gMTAwLCAKICAgICAgIHdpZHRoID0gMTY2LCAKICAgICAgIGxpbWl0c2l6ZSA9IEZBTFNFKQpgYGAKCgoKIyMjIyBmYWNldCBncmlkCmBgYHtyIGZpZy53aWR0aD01LCBmaWcuaGVpZ2h0ID0gM30KZGZfc2FrX2ZhY3RvcjIgfD4KICBnZ3Bsb3QoYWVzKG1jYV94LCBtY2FfeSwgbGFiZWwgPSBzaG9ydF9uYW1lLCBjb2xvdXIgPSBQcmljZSAvIDEwMDApKSArCiAgZ2VvbV9wb2ludChkYXRhID0gZGZfc2FrX2ZhY3RvcjIgfD4gc2VsZWN0KC1jbGFzcyksIGNvbG91ciA9ICJncmV5NzAiKSArCiAgICBnZW9tX3BvaW50KCkgKwogIGdlb21fdGV4dF9yZXBlbCgKICAgICMgZm9yY2UgPSAuMywKICAgICMgZm9yY2VfcHVsbCA9IC41LAogICAgbWF4Lm92ZXJsYXBzID0gNTAsIAogICAgbWF4LnRpbWUgPSAzLCAKICAgIG1heC5pdGVyID0gM2U1LCAKICAgIHNpemUgPSAyLCAKICAgIG1pbi5zZWdtZW50Lmxlbmd0aCA9IC4xLCAKICAgIGZhbWlseSA9ICJGaXJhIENvZGUiCiAgICApICsKICBzY2FsZV9jb2xvdXJfdmlyaWRpc19jKCkgKwogIGxhYnMoY29sb3VyID0gIlByaWNlICgxayB5ZW4pIiApICsKICB4bGFiKCJEaW1lbnNpb24gMSIpICsKICB5bGFiKCJEaW1lbnNpb24gMiIpICsKICBmYWNldF93cmFwKHZhcnMoY2xhc3MpKSArCiAgdGhlbWVfbWluaW1hbChiYXNlX2ZhbWlseSA9ICJGaXJhIENvZGUiKSArCiAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgY29sb3VyID0gImJsYWNrIiksCiAgICAgICAgcGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpKSArCiAgZ3VpZGVzKGNvbG91ciA9IGd1aWRlX2NvbG91cmJhcihiYXJ3aWR0aCA9IDUsIGJhcmhlaWdodCA9IC41KSwgZGlyZWN0aW9uID0gImhvcml6b250YWwiKSArCiAgdGhlbWUobGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgpnZ3NhdmUoZmlsZW5hbWUgPSAib3V0cHV0L3Nha19jb3JyZXNwWFkucGRmIiwgCiAgICAgICAgIGRldmljZSA9IGNhaXJvX3BkZiwKICAgICAgICAgZHBpID0gMzAwLAogICAgICAgICB1bml0ID0gIm1tIiwKICAgICAgIGhlaWdodCA9IDE1MCAsICMgcGl4ZWxz44Gn44Gu44K144Kk44K6CiAgICAgICB3aWR0aCA9IDIwMCwgICMgcGl4ZWxz44Gn44Gu44K144Kk44K6CiAgICAgICBsaW1pdHNpemUgPSBGQUxTRSkKYGBgCgoKCgoKY3V0IHRoZSB0cmVlcwoKaHR0cHM6Ly91Yy1yLmdpdGh1Yi5pby9oY19jbHVzdGVyaW5nCgpgYGB7cn0KCgpkZl9mdml6X3dzcyA8LSBkZl9zYWtfZmFjdG9yIHw+IAogIGZ2aXpfbmJjbHVzdChGVU4gPSBoY3V0LCBtZXRob2QgPSAid3NzIiwgay5tYXggPSAzMCkKCmRmX2Z2aXpfd3NzMiA8LSBkZl9mdml6X3dzcyRkYXRhIHw+IAogIG11dGF0ZShjbHVzdGVycyA9IGFzLm51bWVyaWMoY2x1c3RlcnMpKQpkZl9mdml6X3dzczIgfD4gICAKICBnZ3Bsb3QoYWVzKGNsdXN0ZXJzLCB5KSkgKwogIGdlb21fbGluZSgpICsKICAjIGdlb21fcG9pbnQoKSArCiAgeWxhYigiVG90YWwgV1NTIikgKwogIHhsYWIoIk5vIG9mIGNsdXN0ZXJzIGsiKSArCiAgdGhlbWVfY2xhc3NpYyhiYXNlX2ZhbWlseSA9ICJGaXJhIENvZGUiKQoKICBnZ3NhdmUoZmlsZW5hbWUgPSAib3V0cHV0L3Nha19lbGJvdy5wZGYiLCAKICAgICAgICAgZGV2aWNlID0gY2Fpcm9fcGRmLAogICAgICAgICBkcGkgPSA0NTAsCiAgICAgICAgIHVuaXQgPSAibW0iLAogICAgICAgaGVpZ2h0ID0gNTAsICMgcGl4ZWxz44Gn44Gu44K144Kk44K6CiAgICAgICB3aWR0aCA9IDc1LCAgIyBwaXhlbHPjgafjga7jgrXjgqTjgroKICAgICAgIGxpbWl0c2l6ZSA9IEZBTFNFKQpgYGAKCmBgYHtyfQpmdml6X3NpbCA8LSBkZl9zYWtfZmFjdG9yIHw+IAogIGZ2aXpfbmJjbHVzdChGVU4gPSBoY3V0LCBtZXRob2QgPSAic2lsaG91ZXR0ZSIsIGsubWF4ID0gMzApCmRmX2Z2aXpfc2lsIDwtIGZ2aXpfc2lsJGRhdGEgfD4gCiAgbXV0YXRlKGNsdXN0ZXJzID0gYXMubnVtZXJpYyhjbHVzdGVycykpCmZ2aXpfc2lsCmRmX2Z2aXpfc2lsIHw+ICAgCiAgZ2dwbG90KGFlcyhjbHVzdGVycywgeSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gMTQsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG91ciA9ICJncmF5OTAiKSArCiAgeWxhYigiQXZnIHNpbCB3aWR0aCIpICsKICB4bGFiKCJObyBvZiBjbHVzdGVycyBrIikgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9mYW1pbHkgPSAiRmlyYSBDb2RlIikKCmdnc2F2ZShmaWxlbmFtZSA9ICJvdXRwdXQvc2FrX3NpbC5wZGYiLCAKICAgICAgIGRldmljZSA9IGNhaXJvX3BkZiwKICAgICAgIGRwaSA9IDQ1MCwKICAgICAgIHVuaXQgPSAibW0iLAogICAgICAgaGVpZ2h0ID0gNTAsICMgcGl4ZWxz44Gn44Gu44K144Kk44K6CiAgICAgICB3aWR0aCA9IDc1LCAgIyBwaXhlbHPjgafjga7jgrXjgqTjgroKICAgICAgIGxpbWl0c2l6ZSA9IEZBTFNFKQoKYGBgCgoKCiMjIFVQR01BCgojIyMgamFjY2FyZCAoZG9udCB1c2UpCgpgYGB7cn0Kc2FrX2Rpc3RfamFjY2FyZCA8LSBkaXN0KGRmX3Nha19mYWN0b3IsIG1ldGhvZCA9ICJiaW5hcnkiKQpzYWtfdXBnbWFfamFjY2FyZCA8LSBzYWtfZGlzdF9qYWNjYXJkIHw+CiAgaGNsdXN0KG1ldGhvZCA9ICJhdmVyYWdlIikgICMgYXZlcmFnZSA9IFVQR01BCnNha191cGdtYV90cmVlX2phY2NhcmQgPC0gc2FrX3VwZ21hX2phY2NhcmQgfD4gCiAgIGFzLnBoeWxvKCkKc2FrX3VwZ21hX3RyZWVfamFjY2FyZCR0aXAubGFiZWwgPC0gcGFzdGUwKGRmX3Nha19mYWN0b3IyJGNsYXNzLCBkZl9zYWtfZmFjdG9yMiRzaG9ydF9uYW1lKQpzYWtfdXBnbWFfdHJlZV9qYWNjYXJkIHw+IAogIHBsb3QucGh5bG8odHlwZSA9ICJ1IiwgdXNlLmVkZ2UubGVuZ3RoID0gVFJVRSwgbGFiNHV0ID0gImF4aWFsIikKCmBgYAoKCgojIyMgaGFtbWluZwoKU3BsaXRzVHJlZTTjga5VbmNvcnJlY3RlZFDjgYxIYW1taW5n6Led6Zui44KJ44GX44GE44Gu44Gn44CBe2N1bHRldm9944KS5L2/44Gj44GmSGFtbWluZ+i3nembouOCkueul+WHuuOBmeOCi+OAguOBl+OBi+OBl+WFqOeEtumBleOBhue1kOaenOOBq+OBquOCi+ODu+ODu+ODu+OBquOBnO+8nwoKYGBge3J9CnNha19kaXN0IDwtIGhhbW1pbmdkaXN0cyhkZl9zYWtfZmFjdG9yKSAjIDVzZWMuIHNvbWV0aW1lcyBpdCdzIGZpbmlzaGVkIHZlcnkgZmFzdCBidXQgaXQgbWVhbnMgaXQncyBub3QgcnVubmluZy4uLj8gaW4gdGhhdCBjYXNlLCByZXN0YXJ0IFIKCnNha191cGdtYSA8LSBzYWtfZGlzdCB8PgogIGhjbHVzdChtZXRob2QgPSAiYXZlcmFnZSIpIHw+ICMgYXZlcmFnZSA9IFVQR01BCiAgIGFzLnBoeWxvKCkKc2FrX3VwZ21hJHRpcC5sYWJlbCA8LSBkZl9zYWskc2hvcnRfbmFtZQpzYWtfdXBnbWEgfD4gCiAgcGxvdC5waHlsbyh0eXBlID0gInUiLCB1c2UuZWRnZS5sZW5ndGggPSBUUlVFLCBsYWI0dXQgPSAiYXhpYWwiKQpgYGAKIyMgTkoKCmBgYHtyfQpzYWtfbmogPC0gc2FrX2Rpc3QgfD4gbmooKQpzYWtfbmokdGlwLmxhYmVsIDwtIGRmX3NhayRzaG9ydF9uYW1lCnNha19uaiB8PiAKICBwbG90LnBoeWxvKHR5cGUgPSAidSIsIHVzZS5lZGdlLmxlbmd0aCA9IFRSVUUsIGxhYjR1dCA9ICJheGlhbCIpCmBgYAoKIyMgam9pbiB0cmFpdHMKCmBgYHtyfQpkZl9zYWtfcm93aWQgPC0gZGZfc2FrIHw+CiAgcm93aWRfdG9fY29sdW1uKHZhciA9ICJub2RlIikgfD4gCiAgbXV0YXRlKG5vZGUgPSBub2RlIHw+IGFzLmludGVnZXIoKSkKYGBgCgojIyMgVVBHTUEKCmBgYHtyfQpkZl9zYWtfdXBnbWEgPC0gc2FrX3VwZ21hIHw+CiAgYXNfdGliYmxlKCkgfD4gCiAgbGVmdF9qb2luKGRmX3Nha19yb3dpZCwgYnkgPSAibm9kZSIpIHw+IAogIHNlbGVjdChwYXJlbnQ6Y2xhc3MsIHNob3J0X25hbWUsIEZ1bmN0aW9ucywgd2lkdGgsIHdlaWdodCwgaWQpIHw+IAogICAgbXV0YXRlKHNob3J0X25hbWUgPSBpZl9lbHNlKGlzLm5hKHNob3J0X25hbWUpLCBub2RlIHw+IGFzLmNoYXJhY3RlcigpLCBzaG9ydF9uYW1lKSkgfD4gCiBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInNob3J0X25hbWUiKSB8PiAKICBtdXRhdGUobm9kZSA9IG5vZGUgfD4gYXMubnVtZXJpYygpKSB8PiAKICBzZWxlY3Qobm9kZSwgY2xhc3MsIGlkLCBGdW5jdGlvbnMsIHdlaWdodCwgd2lkdGgpICMgdGVtcG9yYXJpbHkgbGltaXRlCnNha191cGdtYSR0aXAubGFiZWwgPC0gZGZfc2FrJHNob3J0X25hbWUKYGBgCgojIyMgTkoKCmBgYHtyfQpkZl9zYWtfbmogPC0gc2FrX25qIHw+IAogIGFzX3RpYmJsZSgpIHw+IAogIGxlZnRfam9pbihkZl9zYWtfcm93aWQsIGJ5ID0gIm5vZGUiKSB8PiAKICBzZWxlY3QocGFyZW50OmNsYXNzLCBzaG9ydF9uYW1lLCBGdW5jdGlvbnMsIHdpZHRoLCB3ZWlnaHQsIGlkKSB8PiAKICBtdXRhdGUoc2hvcnRfbmFtZSA9IGlmX2Vsc2UoaXMubmEoc2hvcnRfbmFtZSksIG5vZGUgfD4gYXMuY2hhcmFjdGVyKCksIHNob3J0X25hbWUpKSB8PiAKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInNob3J0X25hbWUiKSB8PiAKICBtdXRhdGUobm9kZSA9IG5vZGUgfD4gYXMubnVtZXJpYygpKSB8PiAKICBzZWxlY3Qobm9kZSwgY2xhc3MsIGlkLCBGdW5jdGlvbnMsIHdlaWdodCwgd2lkdGgpCmBgYAoKCiMjIGFuY2VzdHJhbCBwaGVub3R5cGUgcmVjb25zdHJ1Y3Rpb24gdXNpbmcgZmFzdEFuYwoKcGh5dG9vbHMgZmFzdEFuYwoKCiMjIyBreW91dHN1dQoKYGBge3J9CiMgY3JlYXRlIGEgbmFtZWQgbnVtYmVyIHZlY3RvciBvZiB3ZWlnaHRzIChpbiBncmFtKSBvZiBTQUtzCnNha190cmFpdF93ZWlnaHQgPC0gZGZfc2FrX3VwZ21hIHw+IAogIHNlbGVjdCh3ZWlnaHQpIHw+IAogIGFzLm1hdHJpeCgpCiMgY2hhbmdlIHRoZSBtYXRyaXggaW50byBhIHZlY3RvciwgdGhlbiBjdWxsIHVud2FudGVkIG5vZGVzIGFmdGVyIDU3CnNha190cmFpdF93ZWlnaHRfbmFtZWRfbnVtIDwtIHNha190cmFpdF93ZWlnaHRbLCAxXVsxOm5yb3coZGZfc2FrKV0KYGBgCgojIyMgVVBHTUEKCmBgYHtyfQojIG5vdyBmaXQgdGhlIGRhdGEgdG8gbm9kZXMgdXNpbmcgVVBHTUEgdHJlZQpzYWtfZml0X3VwZ21hX3dlaWdodCA8LSBmYXN0QW5jKAogIHNha191cGdtYSwgCiAgc2FrX3RyYWl0X3dlaWdodF9uYW1lZF9udW0sCiAgdmFycyA9IFRSVUUsIAogIENJID0gVFJVRSkKZGZfc2FrX3VwZ21hJHdlaWdodFsobnJvdyhkZl9zYWspICsgMSk6IG5yb3coZGZfc2FrX3VwZ21hKV0gPC0gc2FrX2ZpdF91cGdtYV93ZWlnaHQkYWNlCmBgYAoKIyMjIG5qCmBgYHtyfQpzYWtfZml0X25qX3dlaWdodCA8LSBmYXN0QW5jKAogIHNha19uaiwgCiAgc2FrX3RyYWl0X3dlaWdodF9uYW1lZF9udW0sCiAgdmFycyA9IFRSVUUsCiAgQ0kgPSBUUlVFKQpkZl9zYWtfbmokd2VpZ2h0Wyhucm93KGRmX3NhaykgKyAxKTogbnJvdyhkZl9zYWtfbmopXSA8LSBzYWtfZml0X25qX3dlaWdodCRhY2UKYGBgCgoKIyMgam9pbiB0cmVlIHdpdGggZGYKCmBgYHtyfQojIGpvaW4gd2l0aCB0cmVlLiBzZWVtcyBpbXBvc3NpYmxlIGJ1dCBwb3NzaWJsZSA6RApzYWtfdXBnbWEyIDwtIGZ1bGxfam9pbihzYWtfdXBnbWEsIGRmX3Nha191cGdtYSwgYnkgPSAibm9kZSIpCnNha19uajIgPC0gZnVsbF9qb2luKHNha19uaiwgZGZfc2FrX25qLCBieSA9ICJub2RlIikKYGBgCgoKIyMgUGxvdCB1c2luZyBnZ3RyZWUKCiMjIyBVUEdNQSBoYW1taW5nIAoKYGBge3Igd2FybmluZz1GQUxTRX0Kc2FrX3VwZ21hMiB8PiAKICBnZ3RyZWUobGF5b3V0ID0gImFwZSIsIGFlcyhjb2xvdXIgPSB3ZWlnaHQpKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUwKGNsYXNzLCBsYWJlbCksKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAyLCBoanVzdCA9IC0uMTUpICsgIyBtb2RlbCBuYW1lIChlLmcuLCBFeHBsb3JlcikKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSB3ZWlnaHQgfD4gcm91bmQoMCkpLCBmYW1pbHkgPSAiRmlyYSBDb2RlIiwgc2l6ZSA9IDEsIGhqdXN0ID0gMSkgKyAjIHdlaWdodCBvZiBtb2RlbHMKICBnZW9tX25vZGVsYWIoYWVzKGxhYmVsID0gd2VpZ2h0IHw+IHJvdW5kKDApKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAxLCBjb2xvdXIgPSAiYmxhY2siKSArICMgZXN0aW1hdGVkIHdlaWdodHMgYXQgbm9kZXMKICBnZW9tX3RyZWVzY2FsZSgpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2MobGltaXRzID0gYygxNSwgMjA2KSkgKwogIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gJ29mZicpICsKICB0aGVtZV90cmVlKAogICAgcGxvdC5tYXJnaW49bWFyZ2luKDQwLCA4MCwgNDAsIDYwKSwgCiAgKSArCiAgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSBjKDAuMDUsIC44KSwKICAgIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMiwgJ21tJyksCiAgICApIAoKZ2dzYXZlKGZpbGVuYW1lID0gIm91dHB1dC9zYWtfdXBnbWFfaGFtbWluZy5wZGYiLCAKICAgICAgIGRldmljZSA9IGNhaXJvX3BkZiwKICAgICAgIGRwaSA9IDQ1MCwKICAgICAgIHVuaXQgPSAibW0iLAogICAgICAgaGVpZ2h0ID0gMTAwLCAjIHBpeGVsc+OBp+OBruOCteOCpOOCugogICAgICAgd2lkdGggPSAxNjAsICAjIHBpeGVsc+OBp+OBruOCteOCpOOCugogICAgICAgbGltaXRzaXplID0gRkFMU0UpCmBgYAoKYGBge3J9CnNha191cGdtYTIgfD4gCiAgZ2d0cmVlKGxheW91dCA9ICJjaXJjdWxhciIsIGFlcyhjb2xvdXIgPSB3ZWlnaHQpKSArCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gcGFzdGUwKGNsYXNzLCBsYWJlbCksKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAyLCBoanVzdCA9IC0uMTUpICsgIyBtb2RlbCBuYW1lIChlLmcuLCBFeHBsb3JlcikKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSB3ZWlnaHQgfD4gcm91bmQoMCkpLCBmYW1pbHkgPSAiRmlyYSBDb2RlIiwgc2l6ZSA9IDEsIGhqdXN0ID0gMSkgKyAjIHdlaWdodCBvZiBtb2RlbHMKICBnZW9tX25vZGVsYWIoYWVzKGxhYmVsID0gd2VpZ2h0IHw+IHJvdW5kKDApKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAxLCBjb2xvdXIgPSAiYmxhY2siKSArICMgZXN0aW1hdGVkIHdlaWdodHMgYXQgbm9kZXMKICBnZW9tX3RyZWVzY2FsZSgpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2MobGltaXRzID0gYygxNSwgMjA2KSkgKwogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAnb2ZmJykgKwogIHRoZW1lX3RyZWUoCiAgICBwbG90Lm1hcmdpbj1tYXJnaW4oNDAsIDgwLCA0MCwgNjApLCAKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4wNSwgLjgpLAogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgyLCAnbW0nKSwKICAgICkgCgoKYGBgCgojIyMgTkoKCmBgYHtyIGZpZy53aWR0aCA9MywgZmlnLmhlaWdodD0zLCB3YXJuaW5nPUZBTFNFfQpzYWtfbmoyIHw+IAogIGdndHJlZShsYXlvdXQgPSAiYXBlIiwgYWVzKGNvbG91ciA9IHdlaWdodCkpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZTAoY2xhc3MsIGxhYmVsKSwpLCBmYW1pbHkgPSAiRmlyYSBDb2RlIiwgc2l6ZSA9IDMsIGhqdXN0ID0gLS4zKSArICMgbW9kZWwgbmFtZSAoZS5nLiwgRXhwbG9yZXIpCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gd2VpZ2h0IHw+IHJvdW5kKDApKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAyLCBoanVzdCA9IDEpICsgIyB3ZWlnaHQgb2YgbW9kZWxzCiAgZ2VvbV9ub2RlbGFiKGFlcyhsYWJlbCA9IHdlaWdodCB8PiByb3VuZCgwKSksIGZhbWlseSA9ICJGaXJhIENvZGUiLCBzaXplID0gMiwgdmp1c3QgPSAxKSArICMgZXN0aW1hdGVkIHdlaWdodHMgYXQgbm9kZXMKICBnZW9tX3RyZWVzY2FsZSgpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2MobGltaXRzID0gYygxNSwgMjA2KSkgKwogIGNvb3JkX2NhcnRlc2lhbihjbGlwID0gJ29mZicpICsKICB0aGVtZV90cmVlKAogICAgcGxvdC5tYXJnaW49bWFyZ2luKDEyMCwgNDAsIDQwLCA0MCksIAogICkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gYyguMSwgLjkpLAogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgyLCAnbW0nKSwKICAgICkgCgpgYGAKYGBge3IgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDV9CnNha19uajIgfD4gCiAgcm9vdChvdXRncm91cCA9IDQ1LCBlZGdlbGFiZWwgPSBUUlVFKSB8PiAKICBnZ3RyZWUoYWVzKGNvbG91ciA9IHdlaWdodCkpICsKICBnZW9tX3RpcGxhYihhZXMobGFiZWwgPSBwYXN0ZTAoY2xhc3MsIGxhYmVsKSwpLCBmYW1pbHkgPSAiRmlyYSBDb2RlIiwgc2l6ZSA9IDMsIGhqdXN0ID0gLS4zKSArICMgbW9kZWwgbmFtZSAoZS5nLiwgRXhwbG9yZXIpCiAgZ2VvbV90aXBsYWIoYWVzKGxhYmVsID0gd2VpZ2h0IHw+IHJvdW5kKDApKSwgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAyLCBoanVzdCA9IDEpICsgIyB3ZWlnaHQgb2YgbW9kZWxzCiAgZ2VvbV9ub2RlbGFiKGFlcyhsYWJlbCA9IHdlaWdodCB8PiByb3VuZCgwKSksIGZpbGwgPSAid2hpdGUiLCAgZmFtaWx5ID0gIkZpcmEgQ29kZSIsIHNpemUgPSAyKSArICMgZXN0aW1hdGVkIHdlaWdodHMgYXQgbm9kZXMKICBnZW9tX3RyZWVzY2FsZSgpICsgCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNSkgKwogIHNjYWxlX2NvbG91cl92aXJpZGlzX2MobGltaXRzID0gYygxNSwgMjA2KSkgKwogICMgY29vcmRfY2FydGVzaWFuKGNsaXAgPSAnb2ZmJykgKwogIHRoZW1lX3RyZWUoCiAgICBwbG90Lm1hcmdpbj1tYXJnaW4oNDAsIDQwLCA0MCwgNDApLCAKICApICsKICB0aGVtZSgKICAgIGxlZ2VuZC5wb3NpdGlvbiA9IGMoMC4xNSwgLjUpLAogICAgbGVnZW5kLmtleS5zaXplID0gdW5pdCgyLCAnbW0nKSwKICAgICkgCmBgYAoKCgpjcmVhdGUgcGh5RGF0IGRhdGEgZm9yIGNhdGVnb3JpY2FsIHBoZW5vdHlwZSByZWNvbnN0cnVjdGlvbgpgYGB7cn0KZGZfc2FrX2ZhY3Rvcl93aXRoX25hbWUgPC0gZGZfc2FrX2ZhY3RvciB8PiAKICBtdXRhdGUobmFtZSA9IGRmX3NhayRzaG9ydF9uYW1lKSB8PiAKICByZWxvY2F0ZShuYW1lKQoKcGh5RGF0X3NhayA8LQogIGRmX3Nha19mYWN0b3Jfd2l0aF9uYW1lIHw+IAogIHJvd25hbWVzX3RvX2NvbHVtbigpIHw+IAogIHBpdm90X2xvbmdlcigtbmFtZSwgJ3ZhcmlhYmxlJywgJ3ZhbHVlJykgfD4gCiAgcGl2b3Rfd2lkZXIodmFyaWFibGUsIG5hbWUpIHw+IAogIGZpbHRlcih2YXJpYWJsZSAhPSAicm93bmFtZSIpIHw+IAogIG11dGF0ZShhY3Jvc3MoZXZlcnl0aGluZygpLCBhcy5jaGFyYWN0ZXIpKSB8PiAKICBjb2x1bW5fdG9fcm93bmFtZXModmFyID0gInZhcmlhYmxlIikgfD4KICBwaHlEYXQodHlwZSA9ICJVU0VSIiwgbGV2ZWxzID0gYygiMCIsICIxIikpIApgYGAKCgoKIyMjIG5laWdoYm9ybmV0CgpwbG90IGlzbid0IHJlYWxseSBnb29kLiB1c2UgU3BsaXRzVHJlZTQgdXNpbmcgd3JpdGUubmV4dXMoKT8KCmBgYHtyIGZpZy53aWR0aCA9IDUsIGZpZy5oZWlnaHQ9NX0Kc2FrX2Rpc3QyIDwtIHBoeURhdF9zYWsgfD4gZGlzdC5oYW1taW5nKCkgIyBtdXN0IGJlIHBoeURhdApzYWtfbm4gPC0gc2FrX2Rpc3QyIHw+IAogIG5laWdoYm9yTmV0KCkgIyB0YWtlcyAzMHNlYwoKZGZfc2FrX25uIDwtIHNha19ubiB8PiAKICBhc190aWJibGUoKSB8PiAKICBsZWZ0X2pvaW4oZGZfc2FrX3Jvd2lkLCBieSA9ICJub2RlIikgfD4gCiAgc2VsZWN0KHBhcmVudDpjbGFzcywgc2hvcnRfbmFtZSwgRnVuY3Rpb25zLCB3aWR0aCwgd2VpZ2h0LCBpZCkgfD4gCiAgcm93aWRfdG9fY29sdW1uKCkgfD4gCiAgbXV0YXRlKHNob3J0X25hbWUgPSBpZl9lbHNlKGlzLm5hKHNob3J0X25hbWUpLCByb3dpZCB8PiBhcy5jaGFyYWN0ZXIoKSwgc2hvcnRfbmFtZSkpIHw+IAogIGNvbHVtbl90b19yb3duYW1lcyh2YXIgPSAic2hvcnRfbmFtZSIpIHw+CiAgbXV0YXRlKG5vZGUgPSBub2RlIHw+IGFzLm51bWVyaWMoKSkgfD4KICBzZWxlY3Qobm9kZSwgY2xhc3MsIGlkLCBGdW5jdGlvbnMsIHdlaWdodCwgd2lkdGgpCnNha19ubjIgPC0gZnVsbF9qb2luKHNha19ubiwgZGZfc2FrX25uLCBieSA9ICJub2RlIikKYGBgCgojIyMgcGxvdCBOTgoKYGBge3IgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodD01fQpzYWtfbm4gfD4gZ2dzcGxpdG5ldChhZXMoeCwgeSkpKwogIGdlb21fc3BsaXRuZXQoc2l6ZSA9IC4xKSArCiAgZ2VvbV90aXBsYWIyKGRhdGEgPSAgZGZfc2FrX25uLCBhZXMoY29sb3VyID0gY2xhc3MpLCBzaXplID0gMiwgZmFtaWx5ID0gIkZpcmEgQ29kZSIpICsKICB0aGVtZV90cmVlKCkgKwogIHhsaW0oLS4yNSwgLjM1KSArCiAgeWxpbSgtLjI1LC4yNSkKZ2dzYXZlKGZpbGVuYW1lID0gIm91dHB1dC9zYWtfbm4ucGRmIiwgCiAgICAgICBkZXZpY2UgPSBjYWlyb19wZGYsCiAgICAgICBkcGkgPSA0NTAsCiAgICAgICB1bml0ID0gIm1tIiwKICAgICAgIGhlaWdodCA9IDM2MCwgIyBwaXhlbHPjgafjga7jgrXjgqTjgroKICAgICAgIHdpZHRoID0gMzYwLCAgIyBwaXhlbHPjgafjga7jgrXjgqTjgroKICAgICAgIGxpbWl0c2l6ZSA9IEZBTFNFKQpgYGAKCgpgYGB7ciBmaWcud2lkdGggPSA1LCBmaWcuaGVpZ2h0PTV9CmdldHdkKCkKZGZfc2FrX2ZhY3Rvcl93aXRoX25hbWUgfD4gCiAgd3JpdGVfdHN2KCIuL2RhdGEvc2FrX2ZhY3Rvcl9mb3JfbWVzcXVpdGVfbmV4LnRzdiIpCnNha19uaiB8PiB3cml0ZS5uZXh1cygiZGF0YS9zYWtfZmFjdG9yLm5leCIpCmBgYApgYGB7cn0KIyBkYXRhKHllYXN0LCBwYWNrYWdlPSJwaGFuZ29ybiIpCiMgZG0gPC0gcGhhbmdvcm46OmRpc3QubWwoeWVhc3QpCiMgbm5ldCA8LSBwaGFuZ29ybjo6bmVpZ2hib3JOZXQoZG0pCgpgYGAKCgoK